package net.wangds.redis.comp;

import net.wangds.common.base.funcs.Functions;
import net.wangds.common.base.module.Holder;
import net.wangds.data.Lockable;
import net.wangds.log.helper.LogHelper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.Duration;
import java.util.Objects;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * .
 * <p></p>
 *
 * @author 王东石 2020/6/17 9:30.
 */
@Component
public class RedisLock {


    @Autowired
    @SuppressWarnings("unused")
    private RedisTemplate redisTemplate;


    private String buildLockKey(String lockKey){
        return "LOCK_"+lockKey;
    }

    /**
     * 尝试获取分布式锁
     * @param lockKey 锁
     * @param expireTimeSeconds 超期时间
     * @return 是否获取成功,返回id，否则为null.
     */
    @SuppressWarnings("unchecked")
    public String tryLock(String lockKey, int expireTimeSeconds) {
        Objects.requireNonNull(lockKey);
        String uuid = UUID.randomUUID().toString();
        lockKey = buildLockKey(lockKey);
        try {
            ValueOperations<String, String> ops = (ValueOperations<String, String>) redisTemplate.opsForValue();
            Boolean res = ops.setIfAbsent(lockKey, uuid, Duration.ofSeconds(expireTimeSeconds));
            if (res!=null&&res) {
                return uuid;
            } else {
                return null;
            }
        }catch (Exception ex){
            return null;
        }
    }

    /**
     * 释放锁.
     * @param lockKey 锁.
     * @param value 锁的值.
     * @return 是否释放.
     */
    @SuppressWarnings("unchecked")
    public boolean isLock(String lockKey, String value){
        lockKey = buildLockKey(lockKey);
        ValueOperations<String, String> ops = (ValueOperations<String, String>) redisTemplate.opsForValue();
        return StringUtils.equals(ops.get(lockKey), value);
    }

    /**
     * 释放锁.
     * @param lockKey 锁.
     * @param value 锁的值.
     * @return 是否释放.
     */
    @SuppressWarnings("unchecked")
    public boolean unlock(String lockKey, String value){

        lockKey = buildLockKey(lockKey);
        ValueOperations<String, String> ops = (ValueOperations<String, String>) redisTemplate.opsForValue();
        if(isLock(lockKey, value)){
            Boolean res = redisTemplate.delete(lockKey);
            return res==null?false:res;

        }
        return false;
    }


    /**
     * 在获得锁的情况下执行指定的任务，并且在执行后释放锁.
     * @param lockKey 锁id.
     * @param task 任务.
     * @param tryCount 获得锁重试次数.
     * @param lockSleepMs 每次重试的间隔.
     * @param expireTimeSeconds 锁有效期.
     */
    @SuppressWarnings("unused")
    public void whenLock(String lockKey,Runnable task, int tryCount, int lockSleepMs, int expireTimeSeconds){

        Holder<String> hld = Holder.empty();

        Functions.tryRun(()->{
           String value = tryLock(lockKey, expireTimeSeconds);
           if(StringUtils.isEmpty(value)){
               try {
                   Thread.sleep(lockSleepMs);
               } catch (InterruptedException e) {
                   LogHelper.warn(e);
               }
               throw new RuntimeException("无法获取锁，"+lockKey);
           }
        }, tryCount, LogHelper::warn);


        try(CloseableLock lock = CloseableLock.builder().key(lockKey).value(hld.target).build()){
            task.run();
        } catch (IOException e) {
            LogHelper.error(e);
        }
    }

    public <T extends Lockable, R> R whenLock(T target, Function<T, R> onLocked, BiConsumer<T, Throwable> onError){
        if(target==null){
            return null;
        }
        if(onLocked==null){
            return null;
        }
        Holder<R> res = Holder.empty();
        whenLock(target.buildLockKey(), ()->{
            try {
                res.update(onLocked.apply(target));
            }catch (Throwable th){
                if(onError!=null){
                    try {
                        onError.accept(target, th);
                    }catch (Throwable th1){
                        LogHelper.error("异常处理失败",th);
                    }
                }else{
                    LogHelper.error(th);
                }
            }
        }, 3, 500, 3);
        return res.target;
    }

    public <T extends Lockable, R> R whenLock(T target, Function<T,R> onLocked){
        return whenLock(target, onLocked, (t, th)-> LogHelper.error(()->"处理锁定对象:"+target.buildLockKey()+"时发生异常:"+th.getLocalizedMessage(), th));
    }

}
